特定のセキュリティグループが変更されたことをカスタマイズした日本語メールで通知してみた
日本語でメール通知してほしい
こんにちは!AWS事業本部のおつまみです。
みなさん、特定のセキュリティグループが変更されたことを検知して、メール通知してほしいなぁと思ったことはありますか?私はあります。
例えば以下のようなメール通知です。
いい感じの方法がないかなぁと探していると、AWSナレッジセンターと弊社ブログにどんぴしゃな記事を見つけました。
そこで今回はこれらを組み合わせて、特定のセキュリティグループが変更されたことをいい感じの日本語メールで通知させたいと思います!
2024.10.11 追記
以下のご要望がある方は、下記記事をご参考ください。
特定のセキュリティグループ・ポートが変更されたことをカスタマイズした日本語メールで通知してみた
- 特定のセキュリティグループをパラメータで指定したい
- SSH・RDPといった特定ポートのルール変更のみ検知したい
構成図
今回構築する構成です。
セキュリティグループを変更してから、ユーザーにメール通知するまでの流れ
①Amazon EventBridge:セキュリティグループの変更イベントを検知。EventBridgeの入力トランスフォーマーで、メールの件名と本文を設定。
②AWS StepFunctions:Amazon SNS publish API を呼び出し、件名と本文を指定して通知。※SNSをEventBridgeのターゲットに直接指定するとメール件名の指定ができません。
③Amazon SNS:指定したアドレス宛にメール通知。
前提として、CloudTrailが既に有効化されている環境で構築します。
CloudFormationテンプレートを準備
まずは通知元となるセキュリティグループやその他リソースののテンプレートを用意します。
sg-and-other-resources.yml
AWSTemplateFormatVersion: '2010-09-09' Description: "create Security groups and other resources" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: PJPrefix: Type: String VPCCIDR: Type: String Default: "10.1.0.0/16" PublicSubnetACIDR: Type: String Default: "10.1.10.0/24" KeyName: Type: AWS::EC2::KeyPair::KeyName Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# # VPC Create VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "${PJPrefix}-vpc" # InternetGateway Create InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Sub "${PJPrefix}-igw" # IGW Attach InternetGatewayAttachment: Type: "AWS::EC2::VPCGatewayAttachment" Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ------------------------------------------------------------# # PublicSubnet # ------------------------------------------------------------# PublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: 'ap-northeast-1a' VpcId: !Ref VPC CidrBlock: !Ref PublicSubnetACIDR Tags: - Key: Name Value: !Sub "${PJPrefix}-public-subnet-a" # ------------------------------------------------------------# # PublicRouteTable # ------------------------------------------------------------# PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${PJPrefix}-public-route-a" # ------------------------------------------------------------# # SubnetとRoutetableの関連付け # ------------------------------------------------------------# PublicRouteTableAssociation1a: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet # ------------------------------------------------------------# # Routeの指定 # ------------------------------------------------------------# PublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 RouteTableId: !Ref PublicRouteTable GatewayId: !Ref InternetGateway # ------------------------------------------------------------# # SecurityGroup # ------------------------------------------------------------# SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: SecurityGroup GroupDescription: SecurityGroup VpcId: !Ref VPC SGIngress01: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroup IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 104.28.211.105/32 Description: MyIP # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: ImageId: ami-00d101850e971728d KeyName: !Ref KeyName InstanceType: t2.micro NetworkInterfaces: - AssociatePublicIpAddress: "true" DeviceIndex: "0" SubnetId: !Ref PublicSubnet GroupSet: - !Ref SecurityGroup Tags: - Key: Name Value: "${PJPrefix}-ec2" #------------------------------------------------------------------------------# # Outputs #------------------------------------------------------------------------------# Outputs: SecurityGroup: Value: !Ref SecurityGroup Export: Name: !Sub SecurityGroup
テンプレート作成時のポイント
- 通知用のテンプレートでセキュリティグループを指定できるようにOutputセクションで値を出力しています。
続いて、今回のメインとなる通知用のテンプレートです。
sg-change-alarm.yml
AWSTemplateFormatVersion: '2010-09-09' Description: "create Security group change notification" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: PJPrefix: Type: String SNSTopicName: Type: String Default: SecurityGroup_Change_Alarm Email: Type: String Default: email_address Resources: # ------------------------------------------------------------# # SNS Topic # ------------------------------------------------------------# SNSTopic: Type: "AWS::SNS::Topic" Properties: TopicName: !Ref SNSTopicName Tags: - Key: "Name" Value: !Sub ${PJPrefix}-${SNSTopicName} # ------------------------------------------------------------# # SNS Subscription # ------------------------------------------------------------# Subscription: Type: "AWS::SNS::Subscription" Properties: TopicArn: !Ref SNSTopic Endpoint: !Ref Email Protocol: "email" # ------------------------------------------------------------# # SNS TopicPolicy # ------------------------------------------------------------# EventTopicPolicy: Type: 'AWS::SNS::TopicPolicy' Properties: PolicyDocument: Statement: - Effect: Allow Principal: Service: events.amazonaws.com Action: 'sns:Publish' Resource: '*' Topics: - !Ref SNSTopic # ------------------------------------------------------------# # IAMロール (StepFunction用) # ------------------------------------------------------------# PublishRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - states.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: SNSPublish PolicyDocument: Statement: - Effect: Allow Resource: - !Ref SNSTopic Action: - sns:Publish StateMachineExecuteRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - events.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: StateMachineExecute PolicyDocument: Statement: - Effect: Allow Resource: - !Ref SNSStateMachine Action: - states:StartExecution # ------------------------------------------------------------# # EventBridge Rule # ------------------------------------------------------------# EventBridgeRule: Type: AWS::Events::Rule DependsOn: SNSTopic Properties: Description: !Ref SNSTopicName EventBusName: default EventPattern: !Sub - |- { "source": ["aws.ec2"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventSource": ["ec2.amazonaws.com"], "eventName": ["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "RevokeSecurityGroupIngress", "RevokeSecurityGroupEgress"], "requestParameters": { "groupId": ["${SecurityGroup}"] } } } - SecurityGroup: {'Fn::ImportValue': !Sub 'SecurityGroup'} Name: !Ref SNSTopicName Targets: - Arn: !Ref SNSStateMachine Id: step-function RoleArn: !GetAtt StateMachineExecuteRole.Arn InputTransformer: InputPathsMap: "Account" : "$.account" "SecuritygroupId" : "$.detail.requestParameters.groupId" "eventId" : "$.detail.eventID" "time" : "$.time" "userName" : "$.detail.userIdentity.sessionContext.sessionIssuer.userName" "value" : "$.detail" InputTemplate: | { "subject": "セキュリティグループ変更通知", "message": "セキュリティグループ \"<SecuritygroupId>\" が変更されたことを検知しました。 \n詳細は次のとおりです。 \nAccount ID : \"<Account>\" \n発生時間 : \"<time> \"(UTC) \n変更ユーザー名 : \"<userName>\" \nセキュリティグループID : \"<SecuritygroupId>\" \nCloudTrailイベントID : \"<eventId>\" \n\nセキュリティグループの確認方法 \nEC2コンソール画面の左メニュータグより「セキュリティグループ」を選択します。\n検索ウィンドウで「セキュリティグループID」を入力し、セキュリティグループが正しいことを確認してください。 \n\nCloudTrailイベント履歴の確認方法 \nCloudTrailコンソール画面の左メニュータグより「イベント履歴」を選択します。 \n検索ウィンドウで[イベントID]を選択し、「CloudTrailイベントID」入力します。イベント詳細が確認できます。" } # ------------------------------------------------------------# # StepFunction メール本文カスタマイズ用 # ------------------------------------------------------------# SNSStateMachine: Type: "AWS::StepFunctions::StateMachine" Properties: DefinitionString: !Sub |- { "StartAt": "PublishSns", "States": { "PublishSns": { "Type": "Task", "Resource": "arn:aws:states:::sns:publish", "Parameters": { "TopicArn": "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${SNSTopicName}", "Message.$": "$.message", "Subject.$": "$.subject" }, "End": true } } } RoleArn: !GetAtt PublishRole.Arn
テンプレート作成時のポイント
- EventBridgeのルールの
EventPattern
で監視対象のリソースとイベント内容を指定しています。今回はセキュリティグループの変更通知であるAuthorizeSecurityGroupIngress
,AuthorizeSecurityGroupEgress
,RevokeSecurityGroupIngress
,RevokeSecurityGroupEgress
を指定しています。(114〜128行目) -
EventBridgeのルールの
InputTransformer
でメールに通知する値の取得、およびメール件名・メッセージを設定しています。InputPathsMap
ではメールに通知する値を取得するために、EventBridgeに送られてきたJSONログイベントから特定の値を変数として抽出します。セキュリティグループの変更では以下のようなJSONログイベントが送られてきます。
JSONログイベント
{ "eventVersion": "1.08", "userIdentity": { "type": "AssumedRole", "principalId": "AROAVIM5J54ZWHCLMVYDN:iam-user-name", "arn": "arn:aws:sts::123456789012:assumed-role/iam-user-name/iam-user-name", "accountId": "123456789012", "accessKeyId": "ASIAVIM5J54ZQ2I24HGX", "sessionContext": { "sessionIssuer": { "type": "Role", "principalId": "AROAVIM5J54ZWHCLMVYDN", "arn": "arn:aws:iam::123456789012:role/iam-user-name", "accountId": "123456789012", "userName": "iam-user-name" }, "webIdFederationData": {}, "attributes": { "creationDate": "2023-01-25T05:33:28Z", "mfaAuthenticated": "true" } } }, "eventTime": "2023-01-25T06:21:53Z", "eventSource": "ec2.amazonaws.com", "eventName": "RevokeSecurityGroupIngress", "awsRegion": "ap-northeast-1", "sourceIPAddress": "104.28.211.105", "userAgent": "AWS Internal", "requestParameters": { "groupId": "sg-0bcd6fbcb3109febe", "ipPermissions": {}, "securityGroupRuleIds": { "items": [ { "securityGroupRuleId": "sgr-04d6f81151aa5f291" } ] } }, "responseElements": { "requestId": "c5cbaee6-2c04-49a3-90d4-fc49269da4d0", "_return": true }, "requestID": "c5cbaee6-2c04-49a3-90d4-fc49269da4d0", "eventID": "e714461b-4431-420b-a0f0-b1cb8c068298", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "123456789012", "eventCategory": "Management", "sessionCredentialFromConsole": "true" }
今回はメール通知に含めたい項目として以下を入力パスに指定しました。(135〜141行目)
"Account" : "$.account" "SecuritygroupId" : "$.detail.requestParameters.groupId" "eventId" : "$.detail.eventID" "time" : "$.time" "userName" : "$.detail.userIdentity.sessionContext.sessionIssuer.userName" "value" : "$.detail"
InputTemplate
ではメール件名およびメッセージを設定しています。Step Functionsを挟むと、テンプレートの文字列に\n
を入れることで改行ができます。※EventBridgeのターゲットにSNSを直接指定した場合は改行されません。(142〜146行目)
InputTemplate: | { "subject": "セキュリティグループ変更通知", "message": "セキュリティグループ \"<SecuritygroupId>\" が変更されたことを検知しました。 \n詳細は次のとおりです。 \nAccount ID : \"<Account>\" \n発生時間 : \"<time> \"(UTC) \n変更ユーザー名 : \"<userName>\" \nセキュリティグループID : \"<SecuritygroupId>\" \nCloudTrailイベントID : \"<eventId>\" \n\nセキュリティグループの確認方法 \nEC2コンソール画面の左メニュータグより「セキュリティグループ」を選択します。\n検索ウィンドウで「セキュリティグループID」を入力し、セキュリティグループが正しいことを確認してください。 \n\nCloudTrailイベント履歴の確認方法 \nCloudTrailコンソール画面の左メニュータグより「イベント履歴」を選択します。 \n検索ウィンドウで[イベントID]を選択し、「CloudTrailイベントID」入力します。イベント詳細が確認できます。" }
- StepFunctionsでAmazon SNS publish API を呼び出すステートマシンを設定します。
Parameters
でSNSトピック、メール件名、本文を指定します。(159~162行目)
いざ検証
1. CloudFromationでデプロイ
CloudFormationで用意したテンプレートをデプロイします。
SNSで設定したメールアドレス宛にサブスクリプションの承認依頼メールが届くため、Confirm subscription
で承認します。
2. セキュリティグループを変更
設定したセキュリティグループのインバウンドルールを編集してみます。
見事メール通知が届きました!
おまけとして、メールに記載されているCloudTrailイベントIDをコンソールで検索してみます。
ここからより詳細な情報が確認できますね。
最後に
今回は特定のセキュリティグループが変更されたことを検知して、メール通知させる方法をご紹介しました。
EventBridgeに連携できるAPIであれば、色々なサービスと連携して通知が出来そうですね!
夢が広がります。
最後までお読みいただきありがとうございました! どなたかのお役に立てれば幸いです。
以上、おつまみ(@AWS11077)でした!
参考
EC2 Linux セキュリティグループに対する変更をモニタリングする
CloudWatchアラームとSNSで日本語の件名・本文のメールを送るためのCloudFormationテンプレートを作ってみた | DevelopersIO
Amazon EventBridge event patterns - Amazon EventBridge